package pci

import "go-os-qemu/pkg/portio"

const (
    cfgAddr = 0xCF8
    cfgData = 0xCFC
)

// Device represents a discovered PCI function.
type Device struct {
    Bus     uint8
    Dev     uint8
    Func    uint8
    Vendor  uint16
    Device  uint16
    Class   uint8
    Subclass uint8
    ProgIF  uint8
}

func configAddress(bus, dev, fn uint8, off uint8) uint32 {
    return 0x80000000 | uint32(bus)<<16 | uint32(dev)<<11 | uint32(fn)<<8 | (uint32(off) & 0xFC)
}

func Read32(bus, dev, fn uint8, off uint8) uint32 {
    portio.Outl(cfgAddr, configAddress(bus, dev, fn, off))
    return portio.Inl(cfgData)
}

func Write32(bus, dev, fn uint8, off uint8, val uint32) {
    portio.Outl(cfgAddr, configAddress(bus, dev, fn, off))
    portio.Outl(cfgData, val)
}

// Enumerate scans buses/devices/functions and returns discovered devices.
func Enumerate() []Device {
    devs := make([]Device, 0, 64)
    for bus := uint8(0); bus < 255; bus++ {
        for dev := uint8(0); dev < 32; dev++ {
            for fn := uint8(0); fn < 8; fn++ {
                vd := Read32(bus, dev, fn, 0x00)
                vendor := uint16(vd & 0xFFFF)
                if vendor == 0xFFFF { // no device
                    if fn == 0 { break }
                    continue
                }
                device := uint16((vd >> 16) & 0xFFFF)
                classReg := Read32(bus, dev, fn, 0x08)
                class := uint8((classReg >> 24) & 0xFF)
                subclass := uint8((classReg >> 16) & 0xFF)
                prog := uint8((classReg >> 8) & 0xFF)
                devs = append(devs, Device{Bus: bus, Dev: dev, Func: fn, Vendor: vendor, Device: device, Class: class, Subclass: subclass, ProgIF: prog})
            }
        }
    }
    return devs
}